<template>
  <view v-if="value" class="tn-select-class tn-select">
    <tn-popup
      v-model="value"
      mode="bottom"
      :popup="false"
      length="auto"
      :safeAreaInsetBottom="safeAreaInsetBottom"
      :maskCloseable="maskCloseable"
      :zIndex="elZIndex"
      @close="close"
    >
      <view class="tn-select__content">
        <!-- 自定义头部 -->
        <slot name="header">
          <!-- 默认头部 -->
          <view class="tn-select__content__header" @touchmove.stop.prevent>
            <view
              class="tn-select__content__header__btn tn-select__content__header--cancel"
              :style="{ color: cancelColor }"
              hover-class="tn-hover-class"
              hover-stay-time="150"
              @tap="getResult('cancel')"
            >{{ cancelText }}</view>
            <view class="tn-select__content__header__title">{{ title }}</view>
            <view
              class="tn-select__content__header__btn tn-select__content__header--confirm"
              :style="{ color: confirmColor }"
              hover-class="tn-hover-class"
              hover-stay-time="150"
              @tap="getResult('confirm')"
            >{{ confirmText }}</view>
          </view>
        </slot>
        
        <!-- 搜索框 -->
        <view v-if="showSearch" class="tn-select__search">
          <view class="tn-select__search__input-wrapper">
            <text class="tn-select__search__icon tn-icon-search"></text>
            <input 
              class="tn-select__search__input" 
              v-model="searchKeyword" 
              placeholder="搜索..." 
              @input="onSearchInput"
            />
            <text 
              v-if="searchKeyword" 
              class="tn-select__search__clear tn-icon-close" 
              @tap="clearSearch"
            ></text>
          </view>
        </view>
        
        <!-- 自定义搜索区域 -->
        <slot name="search"></slot>
        
        <!-- 列表内容 -->
        <view class="tn-select__content__body">
          <picker-view
            class="tn-select__content__body__view"
            :value="defaultSelector"
            @pickstart="pickStart"
            @pickend="pickEnd"
            @change="columnChange"
          >
            <picker-view-column v-for="(item, index) in columnData" :key="index">
              <view class="tn-select__content__body__item" v-for="(sub_item, sub_index) in item" :key="sub_index">
                <view class="tn-text-ellipsis">
                  {{ sub_item[labelName] }}
                </view>
              </view>
            </picker-view-column>
          </picker-view>
        </view>
      </view>
    </tn-popup>
  </view>
</template>

<script>
  export default {
    name: 'tn-select',
    props: {
      value: {
        type: Boolean,
        default: false
      },
      // 列表模式
      // single 单列 multi 多列 multi-auto 多列联动
      mode: {
        type: String,
        default: 'single'
      },
      // 列数据
      list: {
        type: Array,
        default() {
          return []
        }
      },
      // value属性名
      valueName: {
        type: String,
        default: 'value'
      },
      // label属性名
      labelName: {
        type: String,
        default: 'label'
      },
      // 当mode=multi-auto时，children的属性名
      childName: {
        type: String,
        default: 'children'
      },
      // 默认值
      defaultValue: {
        type: Array,
        default() {
          return [0]
        }
      },
      // 顶部标题
      title: {
        type: String,
        default: ''
      },
      // 取消按钮文字
      cancelText: {
        type: String,
        default: '取消'
      },
      // 取消按钮文字颜色
      cancelColor: {
        type: String,
        default: ''
      },
      // 确认按钮文字
      confirmText: {
        type: String,
        default: '确认'
      },
      // 确认按钮文字颜色
      confirmColor: {
        type: String,
        default: ''
      },
      // 点击遮罩关闭
      maskCloseable: {
        type: Boolean,
        default: true
      },
      // 预留安全区域
      safeAreaInsetBottom: {
        type: Boolean,
        default: false
      },
      // zIndex
      zIndex: {
        type: Number,
        default: 0
      },
      // 是否显示搜索框
      showSearch: {
        type: Boolean,
        default: false
      },
      // 标题颜色
      titleColor: {
        type: String,
        default: ''
      },
      // 原始列表数据（用于搜索恢复）
      originalList: {
        type: Array,
        default() {
          return []
        }
      }
    },
    computed: {
      elZIndex() {
        return this.zIndex ? this.zIndex : this.$t.zIndex.popup
      }
    },
    data() {
      return {
        // 列是否还在滑动中，微信小程序如果在滑动中就点确定，结果可能不准确
        moving: false,
        // 用户保存当前列的索引，用于判断下一次变化时改变的列
        defaultSelector: [0],
        // picker-view数据
        columnData: [],
        // 保存用户选择的结果
        selectValue: [],
        // 上一次改变时的index
        lastSelectIndex: [],
        // 列数
        columnNum: 0,
        // 搜索关键词
        searchKeyword: '',
        // 原始列数据（用于搜索还原）
        originalColumnData: []
      }
    },
    watch: {
      // 在select弹起的时候，重新初始化所有数据
      value: {
        handler(val) {
          if (val) {
            // 清空搜索关键词
            this.searchKeyword = ''
            setTimeout(() => this.init(), 10)
          }
        },
        immediate: true
      }
    },
    methods: {
      // 标识滑动开始，只有微信小程序才有这样的事件
      pickStart() {
      	// #ifdef MP-WEIXIN
      	this.moving = true;
      	// #endif
      },
      // 标识滑动结束
      pickEnd() {
      	// #ifdef MP-WEIXIN
      	this.moving = false;
      	// #endif
      },
      init() {
        this.setColumnNum()
        this.setDefaultSelector()
        this.setColumnData()
        this.setSelectValue()
        
        // 保存原始列数据用于搜索
        this.originalColumnData = JSON.parse(JSON.stringify(this.columnData))
      },
      // 获取默认选中列下标
      setDefaultSelector() {
        // 如果没有传入默认值，生成用0填充长度为columnNum的数组
        this.defaultSelector = this.defaultValue.length === this.columnNum ? this.defaultValue : Array(this.columnNum).fill(0)
        this.lastSelectIndex = this.$t.deepClone(this.defaultSelector)
      },
      // 计算列数
      setColumnNum() {
        // 单列的数量为1
        if (this.mode === 'single') this.columnNum = 1
        // 多列时取list的长度
        else if (this.mode === 'multi') this.columnNum = this.list.length
        // 多列联动时，通过遍历list的第一个元素，得出有多少列
        else if (this.mode === 'multi-auto') {
          let num = 1
          let column = this.list
          // 如果存在children属性，再次遍历
          while (column[0][this.childName]) {
            column = column[0] ? column[0][this.childName] : {},
            num++
          }
          this.columnNum = num
        }
      },
      // 获取需要展示在picker中的列数据
      setColumnData() {
        let data = []
        this.selectValue = []
        if (this.mode === 'multi-auto') {
          // 获取所有数据中的第一个元素
          let column = this.list[this.defaultSelector.length ? this.defaultSelector[0] : 0]
          // 通过循环所有列数，再根据设定列的数组，得出当前需要渲染的整个列数组
          for (let i = 0; i < this.columnNum; i++) {
            // 第一列默认为整个list数组
            if (i === 0) {
              data[i] = this.list
              column = column[this.childName]
            } else {
              // 大于第一列时，判断是否有默认选中的，如果没有就用该列的第一项
              data[i] = column
              column = column[this.defaultSelector[i]][this.childName]
            }
          }
        } else if (this.mode === 'single') {
          data[0] = this.list
        } else {
          data = this.list
        }
        this.columnData = data
      },
      // 获取默认选中的值，如果没有设置，则默认选中第一项
      setSelectValue() {
        let tmp = null
        for (let i = 0; i < this.columnNum; i++) {
          tmp = this.columnData[i][this.defaultSelector[i]]
          let data = {
            value: tmp ? tmp[this.valueName] : null,
            label: tmp ? tmp[this.labelName] : null
          }
          // 判断是否存在额外的参数
          if (tmp && tmp.extra) data.extra = tmp.extra
          this.selectValue.push(data)
        }
      },
      // 列选项
      columnChange(event) {
        let index = null
        let columnIndex = event.detail.value
        
        this.selectValue = []
        if (this.mode === 'multi-auto') {
          // 对比前后两个数组，判断变更的是那一列
          this.lastSelectIndex.map((v, idx) => {
            if (v != columnIndex[idx]) index = idx
          })
          this.defaultSelector = columnIndex
          // 当前变化列的下一列的数据，需要获取上一列的数据，同时需要指定是上一列的第几个的children，再往后的
          // 默认是队列的第一个为默认选项
          for (let i = index + 1; i < this.columnNum; i++) {
            this.columnData[i] = this.columnData[i - 1][i - 1 == index ? columnIndex[index] : 0][this.childName]
            this.defaultSelector[i] = 0
          }
          // 在历遍的过程中，可能由于上一步修改this.columnData，导致产生连锁反应，程序触发columnChange，会有多次调用
          // 只有在最后一次数据稳定后的结果是正确的，此前的历遍中，可能会产生undefined，故需要判断
          columnIndex.map((item, index) => {
            let data = this.columnData[index][columnIndex[index]]
            let tmp = {
              value: data ? data[this.valueName] : null,
              label: data ? data[this.labelName] : null
            }
            if (data && data.extra !== undefined) tmp.extra = data.extra
            this.selectValue.push(tmp)
          })
          this.lastSelectIndex = columnIndex
        } else if (this.mode === 'single') {
          let data = this.columnData[0][columnIndex[0]]
          let tmp = {
            value: data ? data[this.valueName] : null,
            label: data ? data[this.labelName] : null
          }
          if (data && data.extra !== undefined) tmp.extra = data.extra
          this.selectValue.push(tmp)
        } else if (this.mode === 'multi') {
          columnIndex.map((item, index) => {
            let data = this.columnData[index][columnIndex[index]]
            let tmp = {
              value: data ? data[this.valueName] : null,
              label: data ? data[this.labelName] : null
            }
            if (data && data.extra !== undefined) tmp.extra = data.extra
            this.selectValue.push(tmp)
          })
        }
      },
      close() {
        this.$emit('input', false)
      },
      getResult(event = null) {
        // #ifdef MP-WEIXIN
        if (this.moving) return;
        // #endif
        
        // 确保在搜索状态下能正确获取选中值
        if (this.showSearch && this.searchKeyword && this.columnData && this.columnData[0] && this.columnData[0].length > 0 && 
            this.defaultSelector && this.defaultSelector.length > 0) {
          
          // 重新获取选中值
          this.selectValue = [];
          let selectedIndex = this.defaultSelector[0];
          
          // 确保索引有效
          if (selectedIndex >= 0 && selectedIndex < this.columnData[0].length) {
            let selectedItem = this.columnData[0][selectedIndex];
            this.selectValue.push({
              value: selectedItem[this.valueName],
              label: selectedItem[this.labelName]
            });
          } else if (this.columnData[0].length > 0) {
            // 如果索引无效但有数据，选择第一项
            let firstItem = this.columnData[0][0];
            this.selectValue.push({
              value: firstItem[this.valueName],
              label: firstItem[this.labelName]
            });
          }
        }
        
        if (event) this.$emit(event, this.selectValue)
        this.close()
      },
      // 处理搜索输入
      onSearchInput() {
        if (!this.searchKeyword) {
          // 恢复原始数据
          this.columnData = JSON.parse(JSON.stringify(this.originalColumnData))
          this.defaultSelector = [0]
          return
        }
        
        if (this.mode === 'single') {
          // 过滤数据
          const keyword = this.searchKeyword.toLowerCase()
          const filteredData = this.originalColumnData[0].filter(item => {
            return item[this.labelName].toLowerCase().includes(keyword)
          })
          
          // 更新数据
          this.columnData = [filteredData]
          
          // 如果有匹配结果，选中第一个
          if (filteredData.length > 0) {
            this.defaultSelector = [0]
          }
        }
        // 多列模式暂不支持搜索
      },
      
      // 清除搜索
      clearSearch() {
        this.searchKeyword = ''
        // 恢复原始数据
        this.columnData = JSON.parse(JSON.stringify(this.originalColumnData))
        this.defaultSelector = [0]
      }
    }
  }
</script>

<style lang="scss" scoped>
  
  .tn-select {
    
    &__content {
      position: relative;
      
      &__header {
        position: relative;
        display: flex;
        flex-direction: row;
        width: 100%;
        height: 90rpx;
        padding: 0 40rpx;
        align-items: center;
        justify-content: space-between;
        box-sizing: border-box;
        font-size: 30rpx;
        background-color: #FFFFFF;
        
        &__btn {
          padding: 16rpx;
          box-sizing: border-box;
          text-align: center;
          text-decoration: none;
        }
        
        &__title {
          color: $tn-font-color;
        }
        
        &--cancel {
          color: $tn-font-sub-color;
        }
        
        &--confirm {
          color: $tn-main-color;
        }
      }
      
      &__body {
        width: 100%;
        height: 500rpx;
        overflow: hidden;
        background-color: #FFFFFF;
        
        &__view {
          height: 100%;
          box-sizing: border-box;
        }
        
        &__item {
          display: flex;
          flex-direction: row;
          align-items: center;
          justify-content: center;
          font-size: 32rpx;
          color: $tn-font-color;
          padding: 0 8rpx;
        }
      }
    }
    
    &__search {
      padding: 20rpx 30rpx;
      background-color: #FFFFFF;
      border-bottom: 1rpx solid #EEEEEE;
      
      &__input-wrapper {
        display: flex;
        align-items: center;
        background-color: #F5F5F5;
        border-radius: 40rpx;
        padding: 16rpx 24rpx;
      }
      
      &__icon {
        font-size: 36rpx;
        color: #888888;
        margin-right: 16rpx;
      }
      
      &__input {
        flex: 1;
        height: 60rpx;
        font-size: 28rpx;
        color: #333333;
        background-color: transparent;
      }
      
      &__clear {
        font-size: 32rpx;
        color: #888888;
        padding: 8rpx;
      }
    }
  }
</style>
